home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / ISSUE20 / CACHE / CachedCombo.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-02-06  |  6.7 KB  |  236 lines

  1. unit CachedCombo;
  2. {
  3.   Author : Neil McClements
  4.   Date   : January '97
  5.   C/right: (c) 1997 N. McClements
  6.   Purpose: A TComboBox descendent which can be populated and
  7.            the items/itemindex cached using file streams
  8. }
  9.  
  10. interface
  11.  
  12. uses
  13.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  14.   StdCtrls, DBCtrls, DBTables, DB;
  15.  
  16. //
  17. // TComboList - holds the items and itemindex briefly whilst written to/retrieved from
  18. // a stream
  19.  
  20. type TComboList=class(TComponent)
  21.      private
  22.         FValues:TStringList;
  23.         FItemIndex:longint;
  24.      public
  25.         constructor Create(AOwner:TComponent);override;
  26.         destructor  Destroy; override;
  27.      published
  28.         property ItemValues:TStringList read Fvalues write Fvalues;
  29.         property SelectedItem:longint read FItemIndex write FItemIndex;
  30. end;
  31.  
  32. //
  33. // TCachedCombo - allows the items to be filled from a datasource and cached
  34. //
  35.  
  36. type
  37.   TCachedCombo = class(TComboBox)
  38.   private
  39.     FConfigFile:string;
  40.     FItemsFieldDataLink:TFieldDataLink;
  41.     FUseCache:boolean;
  42.     function  GetItemsDataField:string;
  43.     function  GetItemsDataSource:TDataSource;
  44.     procedure ItemsDataChange(Sender:TObject);
  45.     procedure SetItemsDataField(const theFieldName:string);
  46.     procedure SetItemsDataSource(theSource:TDataSource);
  47.   protected
  48.     procedure Notification(AComponent: TComponent; Operation: TOperation);
  49.     procedure SetUseCache(CacheOnOff:boolean);
  50.   public
  51.     constructor Create(Owner:TComponent);override;
  52.     destructor  Destroy; override;
  53.     function    Populate:boolean;
  54.     function    ReadListFromStream:boolean;
  55.     function    WriteListToStream:boolean;
  56.   published
  57.     property ConfigFile:string read FConfigFile write FConfigFile;
  58.     property ItemsDataField:string read GetItemsDataField write SetItemsDataField;
  59.     property ItemsDataSource:TDataSource read GetItemsDataSource write SetItemsDataSource;
  60.     property UseCache:boolean read FUseCache write SetUseCache default false;
  61.   end;
  62.  
  63. procedure Register;
  64.  
  65. implementation
  66.  
  67. procedure Register;
  68. begin
  69.   RegisterComponents('more...', [TCachedCombo]);
  70. end;
  71.  
  72. constructor TComboList.Create(AOwner:TComponent);
  73. begin
  74.      inherited Create(AOwner);
  75.  
  76.      // Prepare memory to receive the items
  77.      Fvalues:=TStringList.create;
  78. end;
  79.  
  80. destructor TComboList.Destroy;
  81. begin
  82.      // Tidy up after the component before calling its ancestor's destructor
  83.      Fvalues.free;
  84.      inherited destroy;
  85. end;
  86.  
  87. constructor TCachedCombo.Create(Owner:TComponent);
  88. var
  89.    exePath:string;
  90. begin
  91.      inherited Create(Owner);
  92.  
  93.      // Prepare the data link
  94.      FItemsFieldDataLink:=TFieldDataLink.Create;
  95.      FItemsFieldDataLink.OnDataChange:=ItemsDataChange;
  96.  
  97.      // Default config file used to cache dates between application sessions
  98.      if FConfigFile='' then
  99.        begin
  100.           exePath:=ExtractFilePath(application.exename);
  101.           FConfigFile:=exePath+'Combo.cfg';
  102.        end;
  103.  
  104.      // Register the date list component so Delphi knows how to handle it in file streams
  105.      RegisterClass(TComboList);
  106. end;
  107.  
  108. destructor TCachedCombo.Destroy;
  109. begin
  110.      // Tidy up after the component before calling its ancestor's destructor
  111.      FItemsFieldDataLink.free;
  112.      inherited Destroy;
  113. end;
  114.  
  115. function TCachedCombo.WriteListToStream:boolean;
  116. var
  117.    stream:TfileStream;
  118.    ComboList:TComboList;
  119. begin
  120.   // Write the combo items to the cache via a file stream
  121.   try
  122.      ComboList:=TComboList.create(Owner);
  123.      ComboList.ItemValues.assign(self.items);
  124.      ComboList.SelectedItem:=ItemIndex;
  125.      // check that if file not found for read then exception handled gracefully!
  126.      stream:=TFileStream.create(FConfigFile, fmCreate or fmOpenWrite);
  127.      stream.WriteComponent(ComboList);
  128.      stream.free;
  129.      ComboList.free;
  130.      Result:=true;
  131.   except
  132.     on E:exception do
  133.        Result:=false;
  134.   end; // except
  135.  
  136. end; // function
  137.  
  138. function TCachedCombo.ReadListFromStream:boolean;
  139. var
  140.    stream:TfileStream;
  141.    ListComponent:TComponent;
  142. begin
  143.   // Reload the combo items from the cache via a file stream
  144.   try
  145.      // check that if file not found for read then exception handled gracefully!
  146.      stream:=TfileStream.create(FConfigFile, fmopenread);
  147.      while not (stream.position = stream.size) do
  148.        begin
  149.          // Reda the next component in the stream and try and determine what is is
  150.          ListComponent:=stream.ReadComponent(nil);
  151.          if (ListComponent is TComboList) then
  152.            begin
  153.             items.assign((ListComponent as TComboList).ItemValues);
  154.             self.ItemIndex:=(ListComponent as TComboList).SelectedItem;
  155.            end;
  156.        end;
  157.      stream.free;
  158.      Result:=true;
  159.   except
  160.     on E:EFOpenError do
  161.        Result:=false;
  162.   end; // except
  163. end;
  164.  
  165. procedure TCachedCombo.SetUseCache(CacheOnOff:boolean);
  166. begin
  167.   // Refresh the combo box whenever the developer switches the component mode from
  168.   // cached to not-cached
  169.  
  170.   if (CacheOnOff=true) then
  171.       ReadListFromStream
  172.   else
  173.       Populate;
  174. end;
  175.  
  176. function TCachedCombo.Populate:boolean;
  177. var
  178.    dSet:TDataset;
  179. begin
  180. // Fill the combo box items string list from the datasource - if the data link is still valid
  181. if (assigned(FItemsFieldDataLink) and (ItemsDataSource<>nil)) then
  182.   begin
  183.     self.clear;
  184.     dSet:=FItemsFieldDataLink.DataSource.Dataset;
  185.     dSet.Active:=true;
  186.     dSet.first;
  187.     while not dSet.eof do
  188.       begin
  189.          self.items.add(dSet.FieldByName(FItemsFieldDataLink.FieldName).AsString);
  190.          dSet.next;
  191.       end;
  192.     // Record the updated details in the cache for next time...
  193.     Result:=WriteListToStream;
  194.   end
  195. else
  196.   Result:=false;
  197. end;
  198.  
  199. // The following functions maintain the datasource and data links references
  200.  
  201. function TCachedCombo.GetItemsDataField:string;
  202. begin
  203.   GetItemsDataField:=FItemsFieldDataLink.FieldName;
  204. end;
  205.  
  206. function TCachedCombo.GetItemsDataSource:TDataSource;
  207. begin
  208.   GetItemsDataSource:=FItemsFieldDataLink.DataSource;
  209. end;
  210.  
  211. procedure TCachedCombo.SetItemsDataField(const theFieldName:string);
  212. begin
  213.   FItemsFieldDataLink.FieldName:=theFieldName;
  214. end;
  215.  
  216. procedure TCachedCombo.SetItemsDataSource(theSource: TDataSource);
  217. begin
  218.   FItemsFieldDataLink.DataSource:=theSource;
  219. end;
  220.  
  221. procedure TCachedCombo.ItemsDataChange(Sender:TObject);
  222. begin
  223.   if FItemsFieldDataLink.Field = nil then
  224.     FUseCache:=ReadListFromStream;
  225. end;
  226.  
  227. procedure TCachedCombo.Notification(AComponent: TComponent; Operation: TOperation);
  228. begin
  229.   // If the datasource is removed from the application, reset the data source reference
  230.   inherited Notification(AComponent, Operation);
  231.   if (Operation = opRemove) and (FItemsFieldDataLink <> nil) and
  232.     (AComponent = ItemsDataSource) then ItemsDataSource := nil;
  233. end;
  234.  
  235. end.
  236.